#pragma once
#include <iostream>
#include <memory>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <strings.h>
#include "Log.hpp"
#include "Epoller.hpp"

static const int defaultport = 8888;
static const int backlog = 32;

using namespace NS_Epoll;

class EpollServer
{
public:
    EpollServer(uint16_t port = defaultport)
        : _port(port), _isrunning(false), _listensock(-1), _epoller(new Epoller())
    {
    }

    void Init()
    {
        // 创建epoll模型
        _epoller->Create();

        // 创建监听sock
        _listensock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensock < 0)
        {
            LOG(Abnormal, "create listen sock error\n");
            exit(1);
        }
        int opt = 1;
        setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
        struct sockaddr_in addr;
        bzero(&addr, sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_port = htons(_port);
        addr.sin_addr.s_addr = INADDR_ANY;
        int n = bind(_listensock, (struct sockaddr *)(&addr), sizeof(addr));
        if (n < 0)
        {
            LOG(Abnormal, "bind listen sock error\n");
            exit(1);
        }

        n = listen(_listensock, backlog);
        if (n < 0)
        {
            LOG(Abnormal, "listen listen sock error\n");
            exit(1);
        }

        // 添加lissock到epoll模型中
        if (!_epoller->Add(_listensock, EPOLLIN))
        {
            LOG(Abnormal, "adding listensock to epoll model failed\n");
            exit(1);
        }
    }

    void EventsHandler(int n)
    {
        events_arr_ptr events_arr = _epoller->GetEventsArr();
        for (int i = 0; i < n; i++)
        {
            int fd = events_arr[i].data.fd;
            int events = events_arr[i].events;
            if (events & EPOLLIN)
            {
                // 读事件就绪
                if (fd == _listensock)
                {
                    // 新连接到来
                    struct sockaddr_in addr;
                    socklen_t len = sizeof(addr);
                    int newsock = accept(_listensock, (struct sockaddr *)(&addr), &len);

                    if (n < 0)
                    {
                        LOG(Error, "new connection acquisition failed\n");
                        continue;
                    }

                    if (!_epoller->Add(newsock, EPOLLIN))
                    {
                        LOG(Error, "New connection added to epoll model failed\n");
                        close(newsock);
                    }
                }
                else
                {
                    // 普通文件描述符读事件就绪
                    // LOG(Debug, "normal file descriptor read event ready\n");
                    char buffer[1024];
                    int n = recv(fd, buffer, sizeof(buffer) - 1, 0);
                    if (n > 0)
                    {
                        buffer[n] = 0;
                        std::cout << "client say# " << buffer << std::endl;
                    }
                    else
                    {
                        if (n == 0)
                        {
                            LOG(Info, "file descriptor %d disconnected\n", fd);
                        }
                        else
                        {
                            LOG(Error, "file descriptor reading failed for file number %d\n", fd);
                        }
                        _epoller->Del(fd);
                        close(fd);
                    }
                }
            }
        }
    }

    void Loop()
    {
        _isrunning = true;
        while (_isrunning)
        {
            int n = _epoller->Wait();
            switch (n)
            {
            case -1:
                LOG(Error, "epoll model waiting for failure\n");
                break;
            case 0:
                LOG(Debug, "no IO event ready\n");
                break;
            default:
                // LOG(Debug, "IO event ready\n");
                EventsHandler(n);
                break;
            }
            _epoller->DebugPrint();
        }
        _isrunning = false;
    }

    ~EpollServer() {}

private:
    int _listensock;
    uint16_t _port;

    unique_ptr<Epoller> _epoller;

    bool _isrunning;
};